import org.junit.Test;

import java.sql.Date;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * Java 8 特性示例
 *
 * HashMap新增红黑树
 * functional interface 函数式接口
 * 使用 :: 关键字来传递方法或者构造函数引用
 * Optional解决NPE
 * Lambda表达式(Lambda expressions)
 * Streams（流）
 * 日期相关 API(Date API)
 */
public class Java8Demo {


    /**
     * Lambda 表达式(lambda expression)是一个匿名函数，Lambda表达式基于数学中的λ演算得名，直接对应于其中的lambda抽象，是一个匿名函数，即没有函数名的函数。
     * 语法：(Parameters0~n) -> { Body }
     */
    @Test
    public void lambdaTest(){
        new Thread(()->{
            System.out.println("lambda run in java8.");
        }).start();
    }


    /**
     * java.util.Stream表示能应用在一组元素上一次执行的操作序列。
     * Stream 操作分为中间操作或者最终操作两种，最终操作返回一特定类型的计算结果，而中间操作返回Stream本身，这样你就可以将多个操作依次串起来。
     * Stream 的创建需要指定一个数据源，比如` java.util.Collection` 的子类，List 或者 Set， Map 不支持。
     * 可执行操作：过滤filter、排序sort、映射map、匹配match、计数count、聚合reduce
     */
    @Test
    public void streamsTest(){
        List<String> stringList = Arrays.asList("ddd2","aaa2","bbb1","aaa1","bbb3","ccc","bbb2","ddd1");

        //过滤通过一个predicate接口来过滤并只保留符合条件的元素，该操作属于中间操作;  forEach是一个最终操作
        System.out.println("=========filter:");
        stringList.stream()
                .filter((s) -> s.startsWith("a"))
                .forEach(System.out::println);

        //排序是一个中间操作，返回的是排序好后的 Stream。如果不指定自定义的 Comparator 则会使用默认排序。 不影响原数据源
        System.out.println("=========sorted:");
        stringList.stream().sorted().filter((s) -> s.startsWith("a")).forEach(System.out::println);

        //中间操作 map 会将元素根据指定的 Function 接口来依次将元素转成另外的对象
        System.out.println("=========map:");
        stringList.stream().map(String::toUpperCase).sorted((a, b) -> b.compareTo(a)).forEach(System.out::println);

        // Stream提供了多种匹配操作，允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作（anyMatch、allMatch、noneMatch）都是 最终操作 ，并返回一个 boolean 类型的值
        System.out.println("=========match:");
        boolean anyStartsWithA = stringList.stream().anyMatch((s) -> s.startsWith("a"));
        System.out.println(anyStartsWithA);

        //测试 Count (计数)操作
        System.out.println("=========Count:");
        long startsWithB = stringList.stream().filter((s -> s.startsWith("b"))).count();
        System.out.println(startsWithB);

        //Reduce (规约)是一个 最终操作 ，允许通过指定的函数来将stream中的多个元素规约为一个元素，规约后的结果是通过Optional 接口表示
        System.out.println("=========Reduce:");
        Optional<String> reduced = stringList.stream().sorted().reduce((s1, s2) -> s1 + "#" + s2);
        reduced.ifPresent(System.out::println);
    }


    /**
     * Clock 类提供了访问当前日期和时间的方法
     * Clock 是时区敏感的，可以用来取代 System.currentTimeMillis() 来获取当前的微秒数。
     * 某一个特定的时间点也可以使用 Instant 类来表示，Instant 类也可以用来创建旧版本的java.util.Date 对象。
     */
    @Test
    public void ClockTest(){
        Clock clock = Clock.systemDefaultZone();
        long millis = clock.millis();  //代替 System.currentTimeMillis()
        System.out.println("System.currentTimeMillis(): " + millis);

        Instant instant = clock.instant(); //代替 Date
        System.out.println("instant to Date: "+Date.from(instant));
    }


    /**
     * jdk1.8中新增了 LocalDate 与 LocalDateTime等类来解决日期处理方法
     * 可以使用Instant代替 Date，LocalDateTime代替 Calendar
     * 其中涉及日期时间的计算等操作，可细研API
     */
    @Test
    public void localDateTimeTest(){
        LocalDate now = LocalDate.now();
        System.out.println("LocalDate.now(): "+now);
        LocalTime now1 = LocalTime.now();
        System.out.println("LocalTime.now(): " + now1);
        LocalDateTime now2 = LocalDateTime.now();
        System.out.println("LocalDateTime.now(): "+now2);
    }


    /**
     * 新的类DateTimeFormatter 来解决日期格式化问题，可代替 SimpleDateFormat。
     */
    @Test
    public void DateTimeFormatterTest(){
        LocalDateTime rightNow=LocalDateTime.now();
        String date=DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(rightNow);
        System.out.println(date);//2019-03-12T16:26:48.29
        DateTimeFormatter formatter=DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss");
        System.out.println("DateTimeFormatter 格式化日期： "+formatter.format(rightNow));//2019-03-12 16:26:48

        String str1 = "2018==04==12 01时06分09秒";
        // 根据需要解析的日期、时间字符串定义解析所用的格式器
        DateTimeFormatter fomatter1 = DateTimeFormatter.ofPattern("yyyy==MM==dd HH时mm分ss秒");
        LocalDateTime dt1 = LocalDateTime.parse(str1, fomatter1);
        System.out.println("DateTimeFormatter 解析字符串中的时间： "+dt1); // 输出 2014-04-12T01:06:09
    }


    /**
     * 时区使用 ZoneId 来表示。时区可以很方便的使用静态方法of来获取到。
     * 抽象类ZoneId（在java.time包中）表示一个区域标识符。 它有一个名为getAvailableZoneIds的静态方法，它返回所有区域标识符。
     */
    @Test
    public void TimezonesTest(){
        //输出所有区域标识符
        System.out.println(ZoneId.getAvailableZoneIds());

        ZoneId zone1 = ZoneId.of("Europe/Berlin");
        ZoneId zone2 = ZoneId.of("Brazil/East");
        System.out.println(zone1.getRules());// ZoneRules[currentStandardOffset=+01:00]
        System.out.println(zone2.getRules());// ZoneRules[currentStandardOffset=-03:00]
    }
}
